#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include "task.hpp"

using namespace std;
const int _max = 5;
template <class T>
class blockqueue
{
public:
    blockqueue(const int maxnum = _max):_cap(maxnum)
    {
        // 完成锁和条件变量的初始化
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_pcond, nullptr);
        pthread_cond_init(&_ccond, nullptr);
    }
    //生产者产生数据之后，放入到这里面
    void _push(const T &in) // 输入型参数 const&
    {
        pthread_mutex_lock(&_mutex);
        //1、判断
        //细节2：充当条件判断必须是while，不能用if
        //当你被唤醒的时候，可能存在异常或者伪唤醒的情况，pthread_cond_broadcast()把所有线程唤醒的话，如果此时只有一个位置，不进行判断的话，后面就容易出错。
        while(is_full())  //如果满了的话，就得等待
        {
            //细节1,pthread_cond_wait()这个函数的第二个参数必须是我们使用的互斥锁！
            //a、pthread_cond_wait:该函数调用的时候，会以原子性的方式，将锁释放，并将自己挂起
            //b、pthread_cond_wait:该函数被唤醒返回的时候，会自动的重新获取你传入的锁
            pthread_cond_wait(&_pcond,&_mutex); //因为生产条件不满足,无法生产，此时我们的生产者进行等待
        }
        //当运行到这里的时候，说明队列是不满的状态
        _q.push(in);   //将data数据存储进去
        //细节3：这个函数可以在临界区内部，也可以放在外部
        pthread_cond_signal(&_ccond); 
        pthread_mutex_unlock(&_mutex);

    }
    //主要作用是在消费者进行消费之后，弹出对应的数据
    void _pop(T *out) // 输出型参数
    {
        
        pthread_mutex_lock(&_mutex);
        //1、判断
        while(is_empty())
        {
            pthread_cond_wait(&_ccond,&_mutex);
        }
        //2、程序走到这里一定不会空
        *out=_q.front();
        _q.pop();

        //3、绝对保证，阻塞队列里面至少有一个空的位置
        pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_mutex);
    }
    ~blockqueue()
    {
        pthread_cond_destroy(&_ccond);
        pthread_cond_destroy(&_pcond);
        pthread_mutex_destroy(&_mutex);
    }
private:
    int is_full()
    {
        return _q.size()==_cap;
    }
    int is_empty()
    {
        return _q.empty();
    }
private:
    int _cap; // 队列能够容纳的最大数量
    queue<T> _q;
    pthread_mutex_t _mutex; // 互斥锁
    pthread_cond_t _ccond;   // 消费者的条件变量
    pthread_cond_t _pcond;   // 生产者的条件变量
};
